home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / misc / volume17 / remind / part03 < prev    next >
Encoding:
Internet Message Format  |  1991-02-19  |  55.6 KB

  1. From: dfs@doe.carleton.ca (David F. Skoll)
  2. Newsgroups: comp.sources.misc
  3. Subject: v17i005:  remind - A replacement for calendar, Part03/04
  4. Message-ID: <1991Feb19.162824.16254@sparky.IMD.Sterling.COM>
  5. Date: 19 Feb 91 16:28:24 GMT
  6. Approved: kent@sparky.imd.sterling.com
  7. X-Checksum-Snefru: f88af471 4e687c5d 9416a1a1 516dee49
  8.  
  9. Submitted-by: David F. Skoll <dfs@doe.carleton.ca>
  10. Posting-number: Volume 17, Issue 5
  11. Archive-name: remind/part03
  12.  
  13. #! /bin/sh
  14. # This is a shell archive.  Remove anything before this line, then feed it
  15. # into a shell via "sh file" or similar.  To overwrite existing files,
  16. # type "sh file -c".
  17. # The tool that generated this appeared in the comp.sources.unix newsgroup;
  18. # send mail to comp-sources-unix@uunet.uu.net if you want that tool.
  19. # If this archive is complete, you will see the following message at the end:
  20. #        "End of archive 3 (of 4)."
  21. # Contents:  cache.c dosubst.c files.c globals.h init.c omits.c
  22. #   test.rem timed.c
  23. # Wrapped by kent@sparky on Tue Feb 19 10:16:40 1991
  24. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  25. if test -f 'cache.c' -a "${1}" != "-c" ; then 
  26.   echo shar: Will not clobber existing file \"'cache.c'\"
  27. else
  28. echo shar: Extracting \"'cache.c'\" \(4966 characters\)
  29. sed "s/^X//" >'cache.c' <<'END_OF_FILE'
  30. X/***************************************************************/
  31. X/*                                                             */
  32. X/* CACHE.C                                                     */
  33. X/*                                                             */
  34. X/* Contains routines for caching reminder file to improve      */
  35. X/* calendar performance.                                       */
  36. X/*                                                             */
  37. X/* By David Skoll - 15 November 1990                           */
  38. X/***************************************************************/
  39. X
  40. X#include <stdio.h>
  41. X#ifndef NO_MALLOC_H
  42. X#include <malloc.h>
  43. X#endif
  44. X#include <string.h>
  45. X#include "defines.h"
  46. X#include "globals.h"
  47. X#include "protos.h"
  48. X#include "cache.h"
  49. X
  50. X/* Define a cached line */
  51. Xtypedef struct cached_line {
  52. X   char *text;
  53. X   struct cached_line *next;
  54. X} Centry;
  55. X
  56. XCentry Cache, *Current;
  57. X
  58. Xstatic int CacheDone, CacheFailed;
  59. X
  60. X/***************************************************************/
  61. X/*                                                             */
  62. X/*  InitCache                                                  */
  63. X/*                                                             */
  64. X/*  Initializes the caching system.                            */
  65. X/*                                                             */
  66. X/***************************************************************/
  67. X#ifdef __STDC__
  68. Xvoid InitCache(void)
  69. X#else
  70. Xvoid InitCache()
  71. X#endif
  72. X{
  73. X   CacheDone   = 0;
  74. X   CacheFailed = 0;
  75. X   Cache.next  = NULL;
  76. X   Current     = &Cache;
  77. X}
  78. X
  79. X/***************************************************************/
  80. X/*                                                             */
  81. X/* GetLine                                                     */
  82. X/*                                                             */
  83. X/* This function either reads a line from the file, or gets    */
  84. X/* it from memory if it is cached.                             */
  85. X/*                                                             */
  86. X/* Returns 0 if more data to be read; otherwise, non-zero.     */
  87. X/*                                                             */
  88. X/*                                                             */
  89. X/***************************************************************/
  90. X#ifdef __STDC__
  91. Xint GetLine(void)
  92. X#else
  93. Xint GetLine()
  94. X#endif
  95. X{
  96. X   int ret;
  97. X   Token tok;
  98. X   char *s;
  99. X   Centry *c;
  100. X
  101. X   if (CacheFailed) return ReadLine();
  102. X
  103. X   if (!CacheDone) {
  104. X      ret = ReadLine();
  105. X      if (ret) {
  106. X         CacheDone = 1;
  107. X         strcpy(FileName, "* cache *");
  108. X     CurLine = 0;
  109. X         return ret;
  110. X      }
  111. X      /* Check if we should cache this line */
  112. X
  113. X      s = Line;
  114. X      tok = ParseToken(&s);
  115. X      if (tok.type == Clear_t || tok.type == Push_t ||
  116. X          tok.type == Pop_t || tok.type == Rem_t || tok.type == Omit_t) { 
  117. X         c = (Centry *) malloc(sizeof(Centry));
  118. X         if (c == NULL) {
  119. X            CacheFailed = 1;
  120. X            DestroyCache();
  121. X            return 0;
  122. X         }
  123. X         c->text = (char *) malloc(strlen(Line)+1);
  124. X         if (c->text == NULL) {
  125. X            CacheFailed = 1;
  126. X        DestroyCache();
  127. X            free(c);
  128. X        return 0;
  129. X         }
  130. X         /* Insert the cache entry */
  131. X         c->next = NULL;
  132. X         strcpy(c->text, Line);
  133. X         Current->next = c;
  134. X         Current = c;
  135. X      }
  136. X      return ret;
  137. X   } else { /* Over here, we've finished caching, so just return the line */
  138. X      if (Current == NULL) return 1;
  139. X      else {
  140. X         strcpy(Line, Current->text);
  141. X         Current = Current->next;
  142. X         return 0;
  143. X      }
  144. X   }
  145. X}
  146. X/***************************************************************/
  147. X/*                                                             */
  148. X/* ResetCache                                                  */
  149. X/* Reset the cache to beginning, or reopen file if caching     */
  150. X/* failed.                                                     */
  151. X/*                                                             */
  152. X/***************************************************************/
  153. X#ifdef __STDC__
  154. Xvoid ResetCache(void)
  155. X#else
  156. Xvoid ResetCache()
  157. X#endif
  158. X{
  159. X   /* Reset the OMIT context */
  160. X   ClearOmitContext();
  161. X
  162. X   /* Get rid of any spurious stacked OMIT contexts */
  163. X   FreeStackedOmits();
  164. X
  165. X   if (CacheFailed) OpenFile(FileName);
  166. X   else Current = Cache.next;
  167. X}
  168. X
  169. X/***************************************************************/
  170. X/*                                                             */
  171. X/* DestroyCache                                                */
  172. X/* Frees all memory used by the cache.                         */
  173. X/*                                                             */
  174. X/***************************************************************/
  175. X#ifdef __STDC__
  176. Xvoid DestroyCache(void)
  177. X#else
  178. Xvoid DestroyCache()
  179. X#endif
  180. X{
  181. X   Centry *p = &Cache;
  182. X   Centry *c = p->next;
  183. X
  184. X   while (c) {
  185. X      if (c->text) free(c->text);
  186. X      p = c;
  187. X      c = c->next;
  188. X      free(p);
  189. X   }
  190. X   Cache.next = NULL;
  191. X}
  192. X
  193. END_OF_FILE
  194. if test 4966 -ne `wc -c <'cache.c'`; then
  195.     echo shar: \"'cache.c'\" unpacked with wrong size!
  196. fi
  197. # end of 'cache.c'
  198. fi
  199. if test -f 'dosubst.c' -a "${1}" != "-c" ; then 
  200.   echo shar: Will not clobber existing file \"'dosubst.c'\"
  201. else
  202. echo shar: Extracting \"'dosubst.c'\" \(8427 characters\)
  203. sed "s/^X//" >'dosubst.c' <<'END_OF_FILE'
  204. X#include <ctype.h>
  205. X#include <stdio.h>
  206. X#include <string.h>
  207. X#include "defines.h"
  208. X#include "globals.h"
  209. X#include "protos.h"
  210. X
  211. X/***************************************************************/
  212. X/*                                                             */
  213. X/*  DOSUBST.C                                                  */
  214. X/*                                                             */
  215. X/*  Performs line substitution for reminders.                  */
  216. X/*                                                             */
  217. X/*  If mode = 0, ignore %" escapes.                            */
  218. X/*  If mode = 1, process %" escapes.  If a RUN type reminder   */
  219. X/*     does not have %" escape, return the empty string.       */
  220. X/*                                                             */
  221. X/***************************************************************/
  222. X
  223. Xstatic char TODAY[] = "today";
  224. Xstatic char TOMORROW[] = "tomorrow";
  225. X
  226. X#ifdef __STDC__
  227. Xint DoSubst(char *src, char *dst, int d, int m, int y, int jul, enum Token_t t, int tim, int mode)
  228. X#else
  229. Xint DoSubst(src, dst, d, m, y, jul, t, tim, mode)
  230. X     char *src;
  231. X     char *dst;
  232. X     int d;
  233. X     int m;
  234. X     int y;
  235. X     int jul;
  236. X     enum Token_t t;
  237. X     int tim;
  238. X     int mode;
  239. X#endif
  240. X{
  241. X   int diff = jul - JulianToday;
  242. X   char c;
  243. X   char *od, *s;
  244. X   int wkday = jul % 7;
  245. X   char *plu, *pm;
  246. X   int curtim = (int) (SystemTime() / 60);
  247. X   int done;
  248. X   int h;
  249. X   int hh;
  250. X   int min;
  251. X   int tdiff;
  252. X   int adiff, mdiff, hdiff;
  253. X   char *mplu, *hplu, *when;
  254. X   
  255. X   if (mode == 1) {
  256. X      s = src;
  257. X      od = src;
  258. X      while (*s) {
  259. X         if (*s == '%' && *(s+1) == '\"') {
  260. X        src = s+2;
  261. X        break;
  262. X     }
  263. X     s++;
  264. X      }
  265. X      if (src == od && t == Run_t) {
  266. X         *dst = 0;
  267. X     return 0;
  268. X      }
  269. X      if (*src == '%' && *(src+1) == '\"') {
  270. X         *dst = 0;
  271. X     return 0;
  272. X      }
  273. X      if (tim != -1) {
  274. X         sprintf(dst, "%02d:%02d ", (tim / 60), (tim % 60));
  275. X     dst += strlen(dst);
  276. X      }
  277. X   }
  278. X       
  279. X   if (tim == -1) tim = curtim;
  280. X   tdiff = tim - curtim;
  281. X   adiff = ABS(tdiff);
  282. X   mdiff = adiff % 60;
  283. X   hdiff = adiff / 60;
  284. X   mplu = (mdiff == 1 ? "" : "s");
  285. X   hplu = (hdiff == 1 ? "" : "s");
  286. X   when = (tdiff < 0 ? "ago" : "from now");
  287. X   
  288. X   h = tim / 60;
  289. X   min = tim % 60;
  290. X
  291. X   if (h >= 12) pm = "pm"; else pm = "am";
  292. X   if (h == 12) hh = 12; else hh = h % 12;
  293. X
  294. X   *dst = 0;
  295. X   
  296. X   switch(d) {
  297. X      case 1:
  298. X      case 21:
  299. X      case 31: plu = "st"; break;
  300. X      
  301. X      case 2:
  302. X      case 22: plu = "nd"; break;
  303. X      
  304. X      case 3:
  305. X      case 23: plu = "rd"; break;
  306. X      
  307. X      default: plu = "th"; break;
  308. X   }
  309. X      
  310. X   
  311. X   while (c = *src++) {
  312. X     if (c == '\n') continue;
  313. X     else if (c != '%') { *dst++ = c; *dst = 0; }
  314. X     else {
  315. X         od = dst;
  316. X         c = *src++;
  317. X     done = 0;
  318. X         if (diff <= 1) {
  319. X            switch(upper(c)) {
  320. X               case 'A':
  321. X               case 'B':
  322. X           case 'C':
  323. X           case 'E':
  324. X           case 'F':
  325. X           case 'G':
  326. X           case 'H':
  327. X           case 'I':
  328. X           case 'J':
  329. X           case 'K':
  330. X           case 'L':
  331. X           case 'U':
  332. X           case 'V': sprintf(dst, "%s", (diff ? TOMORROW : TODAY));
  333. X                 dst += strlen(dst);
  334. X                 done = 1;
  335. X                  break;
  336. X             
  337. X               default: done = 0;
  338. X            }
  339. X     }         
  340. X     
  341. X     if (!done) switch(upper(c)) {
  342. X        case 0: *dst = 0; return 0;
  343. X
  344. X        case '\"': if (mode == 1) {
  345. X                  *dst = 0;
  346. X              return 0;
  347. X               }
  348. X               break;
  349. X        
  350. X           case 'A':
  351. X               sprintf(dst, "on %s, %d %s, %d", DayName[wkday], d,
  352. X                      MonthName[m], y);
  353. X               dst += strlen(dst);
  354. X           break;
  355. X           
  356. X        case 'B':
  357. X               sprintf(dst, "in %d days' time", diff);
  358. X           dst += strlen(dst);
  359. X               break;
  360. X           
  361. X        case 'C':
  362. X               sprintf(dst, "on %s", DayName[wkday]);
  363. X               dst += strlen(dst);
  364. X           break;
  365. X           
  366. X        case 'D':
  367. X           sprintf(dst, "%d", d);
  368. X           dst += strlen(dst);
  369. X               break;
  370. X           
  371. X        case 'E':
  372. X           sprintf(dst, "on %02d/%02d/%04d", d, m+1, y);
  373. X           dst += strlen(dst);
  374. X               break;
  375. X           
  376. X        case 'F':
  377. X           sprintf(dst, "on %02d/%02d/%04d", m+1, d, y);
  378. X           dst += strlen(dst);
  379. X               break;
  380. X
  381. X        case 'G':
  382. X               sprintf(dst, "on %s, %d %s", DayName[wkday], d, MonthName[m]);
  383. X           dst += strlen(dst);
  384. X               break;
  385. X           
  386. X        case 'H':
  387. X               sprintf(dst, "on %02d/%02d", d, m+1);
  388. X           dst += strlen(dst);
  389. X               break;
  390. X
  391. X        case 'I':
  392. X               sprintf(dst, "on %02d/%02d", m+1, d);
  393. X           dst += strlen(dst);
  394. X               break;
  395. X           
  396. X        case 'J':
  397. X               sprintf(dst, "on %s, %s %d%s, %d", DayName[wkday],
  398. X                      MonthName[m], d, plu, y);
  399. X               dst += strlen(dst);
  400. X               break;
  401. X        
  402. X        case 'K':
  403. X           sprintf(dst, "on %s, %s %d%s", DayName[wkday],
  404. X                          MonthName[m], d, plu);
  405. X           dst += strlen(dst);
  406. X               break;
  407. X                         
  408. X            case 'L':
  409. X           sprintf(dst, "on %04d/%02d/%02d", y, m+1, d);
  410. X           dst += strlen(dst);
  411. X               break;
  412. X           
  413. X        case 'M':
  414. X           sprintf(dst, "%s", MonthName[m]);
  415. X           dst += strlen(dst);
  416. X               break;
  417. X           
  418. X        case 'N':
  419. X           sprintf(dst, "%d", m+1);
  420. X           dst += strlen(dst);
  421. X               break;
  422. X
  423. X        case 'O':
  424. X               if (RealToday == JulianToday) sprintf(dst, " (today)");
  425. X               dst += strlen(dst);
  426. X               break;
  427. X           
  428. X        case 'P':
  429. X           sprintf(dst, (diff == 1 ? "" : "s"));
  430. X           dst += strlen(dst);
  431. X               break;
  432. X           
  433. X        case 'Q':
  434. X           sprintf(dst, (diff == 1 ? "'s" : "s'"));
  435. X           dst += strlen(dst);
  436. X               break;
  437. X           
  438. X        case 'R':
  439. X           sprintf(dst, "%02d", d);
  440. X           dst += strlen(dst);
  441. X               break;
  442. X           
  443. X        case 'S':
  444. X           sprintf(dst, plu);
  445. X           dst += strlen(dst);
  446. X               break;
  447. X           
  448. X        case 'T':
  449. X           sprintf(dst, "%02d", m+1);
  450. X           dst += strlen(dst);
  451. X               break;
  452. X           
  453. X        case 'U':
  454. X           sprintf(dst, "on %s, %d%s %s, %d", DayName[wkday], d,
  455. X                          plu, MonthName[m], y);
  456. X           dst += strlen(dst);
  457. X               break;
  458. X           
  459. X        case 'V':
  460. X           sprintf(dst, "on %s, %d%s %s", DayName[wkday], d, plu,
  461. X                          MonthName[m]);
  462. X           dst += strlen(dst);
  463. X               break;
  464. X           
  465. X        case 'W':
  466. X           sprintf(dst, DayName[wkday]);
  467. X           dst += strlen(dst);
  468. X               break;
  469. X           
  470. X        case 'X':
  471. X           sprintf(dst, "%d", diff);
  472. X           dst += strlen(dst);
  473. X               break;
  474. X           
  475. X        case 'Y':
  476. X           sprintf(dst, "%d", y);
  477. X           dst += strlen(dst);
  478. X               break;
  479. X           
  480. X        case 'Z':
  481. X           sprintf(dst, "%d", y % 100);
  482. X           dst += strlen(dst);
  483. X               break;
  484. X
  485. X        case '1':
  486. X               if (tdiff == 0) 
  487. X                  sprintf(dst, "now");
  488. X               else if (hdiff == 0) 
  489. X              sprintf(dst, "%d minute%s %s", mdiff, mplu, when);
  490. X           else if (mdiff == 0)
  491. X              sprintf(dst, "%d hour%s %s", hdiff, hplu, when);
  492. X               else
  493. X              sprintf(dst, "%d hour%s and %d minute%s %s", hdiff, hplu, mdiff, mplu, when);
  494. X           dst += strlen(dst);
  495. X               break;
  496. X
  497. X            case '2':
  498. X               sprintf(dst, "at %d:%02d%s", hh, min, pm);
  499. X           dst += strlen(dst);
  500. X           break;
  501. X
  502. X        case '3': sprintf(dst, "at %02d:%02d", h, min);
  503. X           dst += strlen(dst);
  504. X           break;
  505. X
  506. X        case '4': sprintf(dst, "%d", tdiff);
  507. X           dst += strlen(dst);
  508. X           break;
  509. X           
  510. X        case '5': sprintf(dst, "%d", adiff);
  511. X           dst += strlen(dst);
  512. X           break;
  513. X
  514. X            case '6': sprintf(dst, when);
  515. X           dst += strlen(dst);
  516. X           break;
  517. X
  518. X        case '7': sprintf(dst, "%d", hdiff);
  519. X           dst += strlen(dst);
  520. X           break;
  521. X
  522. X        case '8': sprintf(dst, "%d", mdiff);
  523. X           dst += strlen(dst);
  524. X           break;
  525. X
  526. X        case '9': sprintf(dst, mplu);
  527. X           dst += strlen(dst);
  528. X           break;
  529. X
  530. X        case '0': sprintf(dst, hplu);
  531. X           dst += strlen(dst);
  532. X           break;
  533. X
  534. X            case '!': sprintf(dst, (tdiff >= 0 ? "is" : "was"));
  535. X           dst += strlen(dst);
  536. X           break;
  537. X        
  538. X            case '_': *dst++ = '\n'; *dst = 0;
  539. X           break;
  540. X
  541. X            default:
  542. X           *dst++ = c;
  543. X           *dst = 0;
  544. X     }
  545. X     if (isupper(c)) *od = upper(*od);
  546. X      }
  547. X   }
  548. X   if (t == Msg_t && mode == 0) *dst++ = '\n';
  549. X   *dst = 0;
  550. X   return 0;
  551. X}   
  552. END_OF_FILE
  553. if test 8427 -ne `wc -c <'dosubst.c'`; then
  554.     echo shar: \"'dosubst.c'\" unpacked with wrong size!
  555. fi
  556. # end of 'dosubst.c'
  557. fi
  558. if test -f 'files.c' -a "${1}" != "-c" ; then 
  559.   echo shar: Will not clobber existing file \"'files.c'\"
  560. else
  561. echo shar: Extracting \"'files.c'\" \(8388 characters\)
  562. sed "s/^X//" >'files.c' <<'END_OF_FILE'
  563. X#include <stdio.h>
  564. X#ifndef UNIX
  565. X#include <stdlib.h>
  566. X#endif
  567. X#include <string.h>
  568. X#ifndef NO_MALLOC_H
  569. X#include <malloc.h>
  570. X#endif
  571. X#ifndef UNIX
  572. X#include <dos.h>
  573. X#endif
  574. X#include <fcntl.h>
  575. X#ifdef UNIX
  576. X#include <sys/types.h>
  577. X#include <sys/stat.h>
  578. X#include <time.h>
  579. X#endif
  580. X#include "defines.h"
  581. X#include "globals.h"
  582. X#include "protos.h"
  583. X
  584. X#ifdef __STDC__
  585. Xstatic int PopFile(void);
  586. X#else
  587. Xstatic int PopFile();
  588. X#endif
  589. X
  590. X/***************************************************************/
  591. X/*                                                             */
  592. X/*  FILES.C                                                    */
  593. X/*                                                             */
  594. X/*  All the routines for opening initial file, getting         */
  595. X/*  and settting initial file's date, closing files,           */
  596. X/*  handling INCLUDE commands, etc.                            */
  597. X/*                                                             */
  598. X/***************************************************************/
  599. X
  600. X/* Define the structure for saving info about a file */
  601. Xtypedef struct {
  602. X   long offset;
  603. X   int  curline;
  604. X   char *name;
  605. X} FileSave;
  606. X
  607. X#define MAXINCLUDE 10
  608. X/* Set up array of MAXINCLUDE file save areas */
  609. Xstatic FileSave stack[MAXINCLUDE];
  610. Xstatic int      SP;
  611. X
  612. Xstatic FILE *fp;
  613. X
  614. X/***************************************************************/
  615. X/*                                                             */
  616. X/*  OpenFile                                                   */
  617. X/*                                                             */
  618. X/*  Open the named file, initialize stack, get file date.      */
  619. X/*  If there's a problem, print an error msg and die.          */
  620. X/*                                                             */
  621. X/***************************************************************/
  622. X#ifdef __STDC__
  623. Xvoid OpenFile(char *s)
  624. X#else
  625. Xvoid OpenFile(s)
  626. X     char *s;
  627. X     
  628. X#endif
  629. X{
  630. X   unsigned date, time;
  631. X#ifndef UNIX
  632. X   unsigned handle;
  633. X#endif
  634. X   int d, m, y;
  635. X#ifndef UNIX
  636. X   
  637. X   /* Get the file's modification date */
  638. X   if(_dos_open(s, O_RDONLY, &handle)) {
  639. X      fprintf(stderr, "remind: Can't open %s.\n", s);
  640. X      exit(1);
  641. X#else
  642. X   struct stat t;
  643. X   struct tm *t1;
  644. X      
  645. X   /* Get the file's access date */
  646. X   if (stat(s, &t)) {
  647. X     fprintf(stderr, "remind: Can't find file %s.\n", s);
  648. X     exit(1);
  649. X#endif
  650. X   }
  651. X#ifndef UNIX
  652. X   _dos_getftime(handle, &date, &time);
  653. X   d = date & 0x1F;
  654. X   m = (date >> 5) & 0xF;
  655. X   y = (date >> 9) + 1980;
  656. X#else
  657. X   t1 = localtime(&(t.st_atime));
  658. X#endif
  659. X   
  660. X#ifndef UNIX
  661. X   if (y < BASE) LastRun = 0; else LastRun = Julian(d, m-1, y);
  662. X   _dos_close(handle);
  663. X#else
  664. X   y = t1->tm_year + 1900;
  665. X   m = t1->tm_mon;
  666. X   d = t1->tm_mday;
  667. X   
  668. X   if (y < BASE) LastRun = 0; else LastRun = Julian(d, m, y);
  669. X#endif
  670. X   fp = fopen(s, "r");
  671. X   if (fp == NULL) {
  672. X      fprintf(stderr, "remind: Can't open %s.\n", s);
  673. X      exit(1);
  674. X   }
  675. X   
  676. X   CurLine = 0;
  677. X   strcpy(FileName, s);
  678. X   SP = 0;
  679. X   
  680. X   return;
  681. X}   
  682. X
  683. X/***************************************************************/
  684. X/*                                                             */
  685. X/*  DoInclude                                                  */
  686. X/*                                                             */
  687. X/*  Push the state of the current file and open a new file.    */
  688. X/*                                                             */
  689. X/***************************************************************/
  690. X#ifdef __STDC__
  691. Xvoid DoInclude(char **s)
  692. X#else
  693. Xvoid DoInclude(s)
  694. X     char **s;
  695. X     
  696. X#endif
  697. X{
  698. X   Token tok;
  699. X   tok = ParseToken(s);
  700. X   
  701. X   /* First, check if there's room on the stack */
  702. X   if (SP == MAXINCLUDE) {
  703. X      Eprint("Too many levels of INCLUDE\n");
  704. X      return;
  705. X   }
  706. X   
  707. X   /* Save current data */
  708. X#ifndef UNIX
  709. X   stack[SP].offset = ftell(fp) - 1L;
  710. X#else
  711. X   stack[SP].offset = ftell(fp);
  712. X#endif
  713. X   stack[SP].curline = CurLine;
  714. X   stack[SP].name = (char *) malloc(strlen(FileName)+1);
  715. X   if (stack[SP].name == NULL) {
  716. X      Eprint("Out of memory for INCLUDE\n");
  717. X      return;
  718. X   }
  719. X   strcpy(stack[SP].name, FileName);
  720. X   
  721. X   SP++;
  722. X   
  723. X   /* Close the current file */
  724. X   fclose(fp);
  725. X   
  726. X   /* Open the new file */
  727. X   fp = fopen(tok.str, "r");
  728. X   if (fp == NULL) {
  729. X      Eprint("Can't open %s for INCLUDE\n", tok.str);
  730. X      PopFile();
  731. X      return;
  732. X   }
  733. X   if (Debug || Purge) {
  734. X      Eprint("INCLUDING file %s\n", tok.str);
  735. X   }
  736. X   
  737. X   /* Set the global variables */
  738. X   CurLine = 0;
  739. X   strcpy(FileName, tok.str);
  740. X   return;
  741. X}
  742. X
  743. X/***************************************************************/
  744. X/*                                                             */
  745. X/*  PopFile                                                    */
  746. X/*                                                             */
  747. X/*  Pop to the previous file, if there is one.  Return 0 for   */
  748. X/*  OK, non-zero for no more files.  If we can't pop back      */
  749. X/*  to a file, print an error message and die.                 */
  750. X/*                                                             */
  751. X/***************************************************************/
  752. X#ifdef __STDC__
  753. Xstatic int PopFile(void)
  754. X#else
  755. Xstatic int PopFile()
  756. X#endif
  757. X{
  758. X#ifndef UNIX
  759. X   unsigned handle, date, time;
  760. X   struct dostime_t t;
  761. X#endif
  762. X
  763. X   if (fp) fclose(fp);
  764. X#ifndef UNIX
  765. X   if (!SP) {
  766. X      if (!Debug && !Purge && (JulianToday == RealToday)) {
  767. X         if (_dos_open(FileName, O_RDONLY, &handle)) {
  768. X            fprintf(stderr, "Could not reset date of %s\n", FileName);
  769. X            return 1;
  770. X         }
  771. X     _dos_gettime(&t);
  772. X     date = CurDay;
  773. X     date |= (CurMon + 1) << 5;
  774. X     date |= (CurYear - 1980) << 9;
  775. X     time = t.second / 2;
  776. X     time |= t.minute << 5;
  777. X     time |= t.hour << 11;
  778. X     _dos_setftime(handle, date, time);
  779. X      }
  780. X      return 1;
  781. X   }
  782. X      
  783. X#else
  784. X   if (!SP) return -1;
  785. X         
  786. X#endif
  787. X   SP--;
  788. X   fp = fopen(stack[SP].name, "r");
  789. X   if (fp == NULL) {
  790. X      Eprint("Argh! Can't return to %s from INCLUDE file %s\n", stack[SP].name, FileName);
  791. X      exit(1);
  792. X   }
  793. X#ifndef UNIX
  794. X   if (fseek(fp, stack[SP].offset, SEEK_SET)) {
  795. X#else
  796. X   if (fseek(fp, stack[SP].offset, 0)) {
  797. X#endif
  798. X      Eprint("Argh! Can't fseek %s after returning from INCLUDE file %s\n", stack[SP].name, FileName);
  799. X      exit(1);
  800. X   }
  801. X   
  802. X   if (Debug || Purge) {
  803. X      Eprint("Returning to file %s\n", stack[SP].name);
  804. X   }
  805. X   CurLine = stack[SP].curline;
  806. X   strcpy(FileName, stack[SP].name);
  807. X   free(stack[SP].name);
  808. X   return 0;
  809. X}
  810. X/***************************************************************/
  811. X/*                                                             */
  812. X/*  ReadLine                                                   */
  813. X/*                                                             */
  814. X/*  Reads a line from the file.  If EOF, pops to previous file */
  815. X/*  if there was one.  Returns 0 if more input, non-zero       */
  816. X/*  if no more input.  Updates CurLine.                        */
  817. X/*                                                             */
  818. X/***************************************************************/
  819. Xint ReadLine()
  820. X{
  821. X   int done = 0;
  822. X   int len;
  823. X   
  824. X   Fresh = 1;
  825. X   while (!done) {
  826. X      CurLine++;
  827. X      if (fgets(Line, 512, fp) == NULL) {
  828. X         if (ferror(fp)) Eprint("Error reading %s\n", FileName);
  829. X     if (PopFile()) return 1;
  830. X      } else {
  831. X         len = strlen(Line);
  832. X         /* Remove the newline */
  833. X         if (*Line && (*(Line + len-1)=='\n')) {
  834. X            *(Line + strlen(Line)-1) = 0;
  835. X            len--;
  836. X         }
  837. X         done = 1;
  838. X         while(*Line && (*(Line + len-1) == '\\') && len<512) {
  839. X         *(Line + len-1) = '\n';
  840. X            if (fgets(Line+len, 512-len,fp) == NULL) {
  841. X               *(Line + len) = 0;
  842. X             break;
  843. X        }
  844. X
  845. X        CurLine++;
  846. X        len = strlen(Line);
  847. X            /* Remove the newline */
  848. X            if (*Line && (*(Line + len-1)=='\n')) {
  849. X               *(Line + strlen(Line)-1) = 0;
  850. X               len--;
  851. X            }
  852. X         }
  853. X      }     
  854. X   }
  855. X   return 0;
  856. X}
  857. X
  858. X/***************************************************************/
  859. X/*                                                             */
  860. X/*  TopLevel - Returns 1 if current file is top level, 0       */
  861. X/*  if it is INCLUDEd.                                         */
  862. X/*                                                             */
  863. X/***************************************************************/
  864. X#ifdef __STDC__
  865. Xint TopLevel(void) { return (SP == 0); }
  866. X#else
  867. Xint TopLevel()
  868. X{
  869. X  return (SP == 0);
  870. X}
  871. X#endif
  872. END_OF_FILE
  873. if test 8388 -ne `wc -c <'files.c'`; then
  874.     echo shar: \"'files.c'\" unpacked with wrong size!
  875. fi
  876. # end of 'files.c'
  877. fi
  878. if test -f 'globals.h' -a "${1}" != "-c" ; then 
  879.   echo shar: Will not clobber existing file \"'globals.h'\"
  880. else
  881. echo shar: Extracting \"'globals.h'\" \(1469 characters\)
  882. sed "s/^X//" >'globals.h' <<'END_OF_FILE'
  883. X/***************************************************************/
  884. X/*                                                             */
  885. X/*  GLOBALS.H                                                  */
  886. X/*                                                             */
  887. X/*  Global variables for REMIND.                               */
  888. X/*                                                             */
  889. X/*  By David Skoll - 30 Sept. 1990                             */
  890. X/*                                                             */
  891. X/***************************************************************/
  892. X
  893. Xextern char *MonthName[];
  894. Xextern char *DayName[];
  895. Xextern Token keywd[];
  896. Xextern int   MonthDays[];
  897. Xextern int   MonthIndex[2][12];
  898. Xextern int   FullOmitArray[];
  899. Xextern int   PartOmitArray[];
  900. Xextern char  Line[];
  901. Xextern char  WorkBuf[];
  902. Xextern char  TmpBuf[];
  903. Xextern char  Fresh;
  904. Xextern char  Purge;
  905. Xextern char  Debug;
  906. Xextern char  Verbose;
  907. Xextern char  Next;
  908. Xextern char  FileName[];
  909. Xextern int   CurLine;
  910. Xextern int   NumEmitted;
  911. Xextern int   NumRem;
  912. Xextern int   NumFullOmit;
  913. Xextern int   NumPartOmit;
  914. Xextern int   JulianToday;
  915. Xextern int   LastRun;
  916. Xextern int   CurYear;
  917. Xextern int   CurMon;
  918. Xextern int   CurDay;
  919. Xextern char  Banner[];
  920. Xextern int   RealToday;
  921. Xextern char  IgRun;
  922. Xextern char  IgOnce;
  923. Xextern int   NumAtsQueued;
  924. Xextern char  QueueAts;
  925. Xextern char  PrintAts;
  926. Xextern int   Calendar;
  927. Xextern int   CalTime;
  928. Xextern int   CalWidth;
  929. Xextern int   SimpleCalendar;
  930. END_OF_FILE
  931. if test 1469 -ne `wc -c <'globals.h'`; then
  932.     echo shar: \"'globals.h'\" unpacked with wrong size!
  933. fi
  934. # end of 'globals.h'
  935. fi
  936. if test -f 'init.c' -a "${1}" != "-c" ; then 
  937.   echo shar: Will not clobber existing file \"'init.c'\"
  938. else
  939. echo shar: Extracting \"'init.c'\" \(6305 characters\)
  940. sed "s/^X//" >'init.c' <<'END_OF_FILE'
  941. X#include <stdio.h>
  942. X#ifndef UNIX
  943. X#include <stdlib.h>
  944. X#endif
  945. X#include <string.h>
  946. X#include "defines.h"
  947. X#include "globals.h"
  948. X#include "protos.h"
  949. X
  950. X#define PATCHLEVEL 0
  951. X
  952. Xstatic char DPMsg[] = "Debug and Purge options conflict - Purge chosen.\n";
  953. Xstatic char DPCMsg[] = "Calendar overrides Debug and Purge options.\n";
  954. Xstatic char NMsg[] = "Next overrides Calendar, Debug and Purge options.\n";
  955. X
  956. X/***************************************************************/
  957. X/*                                                             */
  958. X/*  void initialize(int argc, char *argv[])                    */
  959. X/*                                                             */
  960. X/*  Reads command line options, sets appropriate flags         */
  961. X/*  and FileName.  Also exits with error if invoked            */
  962. X/*  incorrectly.                                               */
  963. X/*                                                             */
  964. X/***************************************************************/
  965. X#ifdef __STDC__
  966. Xvoid initialize(int argc, char *argv[])
  967. X#else
  968. Xvoid initialize(argc, argv)
  969. X     int argc;
  970. X     char *argv[];
  971. X#endif
  972. X{
  973. X   int i;
  974. X   char *s;
  975. X   int d, m, y, t;
  976. X   Token tok;
  977. X
  978. X   Debug    = 0;
  979. X   Purge    = 0;
  980. X   Verbose  = 0;
  981. X   IgOnce   = 0;
  982. X   IgRun    = 0;
  983. X   Calendar = 0;
  984. X   Next     = 0;
  985. X   PrintAts = 1;
  986. X   QueueAts = 1;
  987. X   CalWidth = 10;
  988. X   SimpleCalendar = 0;
  989. X
  990. X   if(argc == 1) {
  991. X     fprintf(stderr, "\nREMIND 2.3 Patch Level %d (C) 1990, 1991 by David Skoll.\n\n", PATCHLEVEL);
  992. X#ifdef UNIX
  993. X     fprintf(stderr, "Usage: remind [-n | -d | -p | -c# [-w# | -s]] [-voraq] filename [date]\n\n");
  994. X#else
  995. X     fprintf(stderr, "Usage: remind [-n | -d | -p | -c# [-w# | -s]] [-vor] filename [date]\n\n");
  996. X#endif
  997. X     fprintf(stderr, "-n   Output next occurrence of reminders in simple format\n");
  998. X     fprintf(stderr, "-d   Debug reminder file\n-p   Purge reminder file\n");
  999. X     fprintf(stderr, "-c#  Produce calendar for # months\n");
  1000. X     fprintf(stderr, "-w#  Make calendar # columns wide\n");
  1001. X     fprintf(stderr, "-s   Produce simple calendar listing (used with -c)\n");
  1002. X     fprintf(stderr, "-v   Verbose messages\n-o   Ignore ONCE directives\n");
  1003. X     fprintf(stderr, "-r   Ignore RUN directives\n");
  1004. X#ifdef UNIX
  1005. X     fprintf(stderr, "-a   Do not trigger current AT reminders in foreground\n");
  1006. X     fprintf(stderr, "-q   Do not queue current AT reminders\n\n");
  1007. X#endif
  1008. X     exit(1);
  1009. X   }
  1010. X
  1011. X   i = 1;
  1012. X   s = argv[i];
  1013. X
  1014. X   /* Process options */
  1015. X   while (*s == '-') {
  1016. X      while (*++s) {
  1017. X         switch(upper(*s)) {
  1018. X
  1019. X        case 'N': Next = 1;
  1020. X                 if (Calendar || Debug || Purge) {
  1021. X                Calendar = Debug = Purge = 0;
  1022. X            fprintf(stderr, NMsg);
  1023. X                 }
  1024. X             break;
  1025. X
  1026. X            case 'P':  Purge = 1;
  1027. X             if (Next) {
  1028. X                Calendar = Debug = Purge = 0;
  1029. X            fprintf(stderr, NMsg);
  1030. X             }
  1031. X             if (Calendar) {
  1032. X                Debug = Purge = 0;
  1033. X            fprintf(stderr, DPCMsg);
  1034. X                 }
  1035. X                     if (Debug) {
  1036. X                        Debug = 0;
  1037. X                        fprintf(stderr, DPMsg);
  1038. X                     }
  1039. X             break;
  1040. X
  1041. X            case 'D': Debug = 1;
  1042. X             if (Next) {
  1043. X                Calendar = Debug = Purge = 0;
  1044. X            fprintf(stderr, NMsg);
  1045. X             }
  1046. X             if (Calendar) {
  1047. X                Debug = Purge = 0;
  1048. X            fprintf(stderr, DPCMsg);
  1049. X                 }
  1050. X                  if (Purge) {
  1051. X                 Debug = 0;
  1052. X             fprintf(stderr, DPMsg);
  1053. X              }
  1054. X              break;
  1055. X
  1056. X            case 'C': Calendar = 1;
  1057. X             if (Next) {
  1058. X                Calendar = Debug = Purge = 0;
  1059. X            fprintf(stderr, NMsg);
  1060. X             }
  1061. X             if (Debug || Purge) {
  1062. X                Debug = Purge = 0;
  1063. X            fprintf(stderr, DPCMsg);
  1064. X                 }
  1065. X                     t = atoi(s + 1);
  1066. X             if (t > 0 && t <= 12) Calendar = t;
  1067. X                     /* Skip remaining chars on this option */
  1068. X                 while (*++s) ;
  1069. X                 s--;
  1070. X             break;
  1071. X
  1072. X        case 'W': CalWidth = (atoi(s+1)-9)/7;
  1073. X                 if (CalWidth < 10) CalWidth = 10;
  1074. X             if (CalWidth > 40) CalWidth = 40;
  1075. X                 while (*++s) ;
  1076. X                 s--;
  1077. X             break;
  1078. X
  1079. X            case 'S': SimpleCalendar = 1; break;
  1080. X
  1081. X        case 'V': Verbose = 1; break;
  1082. X
  1083. X        case 'O': IgOnce = 1; break;
  1084. X
  1085. X        case 'R': IgRun = 1; break;
  1086. X#ifdef UNIX
  1087. X        case 'A': PrintAts = 0; break;
  1088. X
  1089. X            case 'Q': QueueAts = 0; break;          
  1090. X#endif        
  1091. X        default: fprintf(stderr, "Unknown option '%c' ignored.\n", *s);
  1092. X     }                  
  1093. X      }
  1094. X      i++;
  1095. X      if (i >= argc) {
  1096. X    fprintf(stderr, "Missing filename - type 'remind' for usage information.\n");
  1097. X     exit(1);
  1098. X      }
  1099. X      s = argv[i];
  1100. X   }     
  1101. X   
  1102. X   /* Set FileName */
  1103. X   strcpy(FileName, argv[i++]);
  1104. X   
  1105. X   /* Get date, if supplied */
  1106. X   if (i < argc) {
  1107. X      *WorkBuf = 0;
  1108. X      while (i < argc) {
  1109. X         strcat(WorkBuf, argv[i++]);
  1110. X     strcat(WorkBuf, " ");
  1111. X      }
  1112. X      /* Parse the date */
  1113. X      d = m = y = -1;
  1114. X      tok.type = Unknown_t;
  1115. X      s = WorkBuf;
  1116. X      while (tok.type != Eol_t) {
  1117. X         tok = ParseToken(&s);
  1118. X     switch(tok.type) {
  1119. X
  1120. X        case Eol_t: break;
  1121. X
  1122. X        case Year_t: if (y == -1) 
  1123. X                        y = tok.val;
  1124. X             else {
  1125. X                fprintf(stderr, "Year specified twice!\n");
  1126. X                exit(1);
  1127. X             }
  1128. X             break;
  1129. X
  1130. X        case Month_t: if (m == -1) 
  1131. X                        m = tok.val;
  1132. X             else {
  1133. X                fprintf(stderr, "Month specified twice!\n");
  1134. X                exit(1);
  1135. X             }
  1136. X             break;
  1137. X             
  1138. X        case Day_t: if (d == -1) 
  1139. X                        d = tok.val;
  1140. X             else {
  1141. X                fprintf(stderr, "Day specified twice!\n");
  1142. X                exit(1);
  1143. X             }
  1144. X             break;
  1145. X       
  1146. X            default: fprintf(stderr, "Illegal token %s on command line.\n", tok.str);
  1147. X                 exit(1);
  1148. X   
  1149. X         }
  1150. X      } 
  1151. X      
  1152. X      if (d == -1 || m == -1 || y == -1) {
  1153. X         fprintf(stderr, "Date on command line must be fully specified.\n");
  1154. X     exit(1);
  1155. X      }
  1156. X      if (CheckDate(d, m, y)) {
  1157. X         fprintf(stderr, "Illegal date on command line.\n");
  1158. X     exit(1);
  1159. X      }
  1160. X
  1161. X      CurDay = d;
  1162. X      CurMon = m;
  1163. X      CurYear = y;
  1164. X      JulianToday = Julian(d, m, y);
  1165. X   }
  1166. X   OpenFile(FileName);
  1167. X   if (Debug) {
  1168. X      FromJulian(LastRun, &d, &m, &y);
  1169. X#ifndef UNIX
  1170. X      fprintf(stderr, "\nFile %s last modified on %s, %d %s, %d\n", FileName,
  1171. X#else
  1172. X      fprintf(stderr, "\nFile %s last accessed on %s, %d %s, %d\n", FileName,
  1173. X#endif
  1174. X                       DayName[LastRun % 7], d, MonthName[m], y);
  1175. X   }
  1176. X   return;
  1177. X}
  1178. END_OF_FILE
  1179. if test 6305 -ne `wc -c <'init.c'`; then
  1180.     echo shar: \"'init.c'\" unpacked with wrong size!
  1181. fi
  1182. # end of 'init.c'
  1183. fi
  1184. if test -f 'omits.c' -a "${1}" != "-c" ; then 
  1185.   echo shar: Will not clobber existing file \"'omits.c'\"
  1186. else
  1187. echo shar: Extracting \"'omits.c'\" \(10630 characters\)
  1188. sed "s/^X//" >'omits.c' <<'END_OF_FILE'
  1189. X/***************************************************************/
  1190. X/*                                                             */
  1191. X/*  OMITS.C                                                    */
  1192. X/*                                                             */
  1193. X/*  By David Skoll - 12 February 1991                          */
  1194. X/*                                                             */
  1195. X/*  Handle all code related to global OMITs                    */
  1196. X/*                                                             */
  1197. X/***************************************************************/
  1198. X#include <stdio.h>
  1199. X#ifndef UNIX
  1200. X#include <stdlib.h>
  1201. X#endif
  1202. X
  1203. X#ifndef NO_MALLOC_H
  1204. X#include <malloc.h>
  1205. X#endif
  1206. X
  1207. X#include "defines.h"
  1208. X#include "protos.h"
  1209. X#include "globals.h"
  1210. X
  1211. X/* Define an OMIT stack buffer */
  1212. Xtypedef struct omit_stack {
  1213. X   struct omit_stack *next;
  1214. X   int NumFullOmit;
  1215. X   int NumPartOmit;
  1216. X   int Omits[1];
  1217. X} OmitBuffer;
  1218. X
  1219. X/* Variables that we only want visible here */
  1220. Xstatic OmitBuffer *OmitStack = (OmitBuffer *) NULL;
  1221. X
  1222. X/***************************************************************/
  1223. X/*                                                             */
  1224. X/*  int DoGlobalOmit(char **s)                                 */
  1225. X/*                                                             */
  1226. X/*  Add an entry to the global ommissions array.  Either       */
  1227. X/*  a fully-specified date, or a mm-yy type date.              */
  1228. X/*  Return 0 if OK, -1 if date is in past, -2 if problem.      */
  1229. X/*                                                             */
  1230. X/***************************************************************/
  1231. X#ifdef __STDC__
  1232. Xint DoGlobalOmit(char **s)
  1233. X#else
  1234. Xint DoGlobalOmit(s)
  1235. X     char **s;
  1236. X#endif
  1237. X{
  1238. X   int d = -1, m = -1, y = -1;
  1239. X   int omit;
  1240. X   int *ptr;
  1241. X   Token tok;
  1242. X   char *olds = *s;
  1243. X
  1244. X   tok.type = Unknown_t;
  1245. X   while(tok.type != Eol_t && tok.type != Run_t && tok.type != Msg_t) {
  1246. X      tok = ParseToken(s);
  1247. X      switch (tok.type) {
  1248. X     case Year_t:  y = tok.val; break;
  1249. X     case Month_t: m = tok.val; break;
  1250. X     case Day_t:   d = tok.val; break;
  1251. X
  1252. X     case Delta_t:
  1253. X     case Eol_t:
  1254. X     case Msg_t:
  1255. X     case Run_t: break;
  1256. X     default:      Eprint("Invalid token '%s' for OMIT command.\n", tok.str);
  1257. X               return -2;
  1258. X      }
  1259. X   }
  1260. X
  1261. X   if (d == -1 || m == -1 || CheckDate(d, m, y)) {
  1262. X      Eprint("Invalid date specification.\n");
  1263. X      return -2;
  1264. X   }
  1265. X
  1266. X   if (y == -1) { /* Only mm-dd specified */
  1267. X      if (NumPartOmit == POMITSIZE) {
  1268. X     Eprint("Too many partially-specified OMITs.\n");
  1269. X     return -2;
  1270. X      }
  1271. X      omit = (m << 5) + d;
  1272. X      ptr = PartOmitArray + NumPartOmit;
  1273. X      NumPartOmit++;
  1274. X      while (ptr > PartOmitArray && *(ptr-1) > omit) {
  1275. X     *ptr = *(ptr-1);
  1276. X     ptr--;
  1277. X      }
  1278. X      *ptr = omit;
  1279. X
  1280. X      /* Check if the bonehead already has it - if so, delete it */
  1281. X      if (ptr > PartOmitArray && *(ptr-1) == *ptr) {
  1282. X     if (Debug) Eprint("Duplicated partially-specified OMIT.\n");
  1283. X     NumPartOmit--;
  1284. X     while (ptr < NumPartOmit+PartOmitArray) {
  1285. X        *ptr = *(ptr + 1);
  1286. X        ptr++;
  1287. X     }
  1288. X      }
  1289. X      /* If we got a DELTA, a MSG or a RUN, then execute DoRem */
  1290. X      if (tok.type == Delta_t || tok.type == Run_t || tok.type == Msg_t)
  1291. X          return DoRem(&olds);
  1292. X   } else { /* All three specified */
  1293. X      if (NumFullOmit == FOMITSIZE) {
  1294. X     Eprint("Too many fully-specified OMITs.\n");
  1295. X     return -2;
  1296. X      }
  1297. X      omit = Julian(d, m, y);
  1298. X
  1299. X      ptr = FullOmitArray + NumFullOmit;
  1300. X      NumFullOmit++;
  1301. X      while (ptr > FullOmitArray && *(ptr-1) > omit) {
  1302. X     *ptr = *(ptr-1);
  1303. X     ptr--;
  1304. X      }
  1305. X      *ptr = omit;
  1306. X
  1307. X      /* Check if the bonehead already has it - if so, delete it */
  1308. X      if (ptr > FullOmitArray && *(ptr-1) == *ptr) {
  1309. X     if (Debug) Eprint("Duplicated fully-specified OMIT.\n");
  1310. X     NumFullOmit--;
  1311. X     while (ptr < NumFullOmit+FullOmitArray) {
  1312. X        *ptr = *(ptr + 1);
  1313. X        ptr++;
  1314. X     }
  1315. X      }
  1316. X      if (omit < JulianToday) {
  1317. X     if (Debug) Eprint("Omit has expired.\n");
  1318. X     return -1;
  1319. X      }
  1320. X      /* If we got a DELTA, a MSG or a RUN, then execute DoRem */
  1321. X      if (tok.type == Delta_t || tok.type == Run_t || tok.type == Msg_t)
  1322. X         return DoRem(&olds);
  1323. X   }
  1324. X   return 0;
  1325. X}
  1326. X
  1327. X/***************************************************************/
  1328. X/*                                                             */
  1329. X/* PushOmitContext                                             */
  1330. X/*                                                             */
  1331. X/* Push the Global OMIT context onto a stack.                  */
  1332. X/*                                                             */
  1333. X/* Returns 0 for success, 1 for failure.                       */
  1334. X/*                                                             */
  1335. X/***************************************************************/
  1336. X#ifdef __STDC__
  1337. Xint PushOmitContext(void)
  1338. X#else
  1339. Xint PushOmitContext()
  1340. X#endif
  1341. X{
  1342. X   OmitBuffer *new = (OmitBuffer *) malloc( sizeof(OmitBuffer) +
  1343. X                     (NumFullOmit + NumPartOmit - 1) * sizeof(int));
  1344. X
  1345. X   if (!new) {
  1346. X      Eprint("Unable to allocate memory for PUSH-OMIT-CONTEXT.\n");
  1347. X      return 1;
  1348. X   }
  1349. X
  1350. X   new->NumFullOmit = NumFullOmit;
  1351. X   new->NumPartOmit = NumPartOmit;
  1352. X
  1353. X   CopyInts(FullOmitArray, &(new->Omits[0]), NumFullOmit);
  1354. X   CopyInts(PartOmitArray, &(new->Omits[NumFullOmit]), NumPartOmit);
  1355. X
  1356. X   new->next = OmitStack;
  1357. X   OmitStack = new;
  1358. X
  1359. X   return 0;
  1360. X}
  1361. X
  1362. X/***************************************************************/
  1363. X/*                                                             */
  1364. X/* PopOmitContext                                              */
  1365. X/*                                                             */
  1366. X/* Restore the OMIT context from the stack.                    */
  1367. X/*                                                             */
  1368. X/* Returns 0 for success, 1 for failure.                       */
  1369. X/*                                                             */
  1370. X/***************************************************************/
  1371. X#ifdef __STDC__
  1372. Xint PopOmitContext(void)
  1373. X#else
  1374. Xint PopOmitContext()
  1375. X#endif
  1376. X{
  1377. X
  1378. X   OmitBuffer *temp;
  1379. X
  1380. X   if (!OmitStack) {
  1381. X      Eprint("No saved contexts for POP-OMIT-CONTEXT.\n");
  1382. X      return 1;
  1383. X   }
  1384. X
  1385. X   NumFullOmit = OmitStack->NumFullOmit;
  1386. X   NumPartOmit = OmitStack->NumPartOmit;
  1387. X
  1388. X   CopyInts(&(OmitStack->Omits[0]), FullOmitArray, NumFullOmit);
  1389. X   CopyInts(&(OmitStack->Omits[NumFullOmit]), PartOmitArray, NumPartOmit);
  1390. X
  1391. X   temp = OmitStack->next;
  1392. X   free(OmitStack);
  1393. X   OmitStack = temp;
  1394. X   return 0;
  1395. X}
  1396. X
  1397. X/***************************************************************/
  1398. X/*                                                             */
  1399. X/*  ClearOmitContext                                           */
  1400. X/*                                                             */
  1401. X/*  Get rid of all global OMITS.                               */
  1402. X/*                                                             */
  1403. X/***************************************************************/
  1404. X#ifdef __STDC__
  1405. Xvoid ClearOmitContext(void)
  1406. X#else
  1407. Xvoid ClearOmitContext()
  1408. X#endif
  1409. X{
  1410. X   NumFullOmit = NumPartOmit = 0;
  1411. X}
  1412. X
  1413. X/***************************************************************/
  1414. X/*                                                             */
  1415. X/*  CopyInts                                                   */
  1416. X/*                                                             */
  1417. X/*  Copy integer values from one array to another.             */
  1418. X/*                                                             */
  1419. X/***************************************************************/
  1420. X#ifdef __STDC__
  1421. Xvoid CopyInts(int *from, int *to, int count)
  1422. X#else
  1423. Xvoid CopyInts(from, to, count)
  1424. Xint *from, *to, count;
  1425. X#endif
  1426. X{
  1427. X   while (count--) *to++ = *from++;
  1428. X}
  1429. X
  1430. X/***************************************************************/
  1431. X/*                                                             */
  1432. X/*  FreeStackedOmits                                           */
  1433. X/*                                                             */
  1434. X/*  Get rid of all the stacked OMIT contexts                   */
  1435. X/*                                                             */
  1436. X/***************************************************************/
  1437. X#ifdef __STDC__
  1438. Xvoid FreeStackedOmits(void)
  1439. X#else
  1440. Xvoid FreeStackedOmits()
  1441. X#endif
  1442. X{
  1443. X   OmitBuffer *current = OmitStack, *next;
  1444. X
  1445. X   if (current && Debug) Eprint("Warning - more PUSH-OMIT-CONTEXTs than POP-OMIT-CONTEXTs\n");
  1446. X
  1447. X   while (current) {
  1448. X      next = current->next;
  1449. X      free(current);
  1450. X      current = next;
  1451. X   }
  1452. X
  1453. X   OmitStack = (OmitBuffer *) NULL;
  1454. X}
  1455. X
  1456. X/***************************************************************/
  1457. X/*                                                             */
  1458. X/* IsOmitted                                                   */
  1459. X/*                                                             */
  1460. X/* Returns non-zero if the julian date should be omitted, 0    */
  1461. X/* otherwise.                                                  */
  1462. X/*                                                             */
  1463. X/***************************************************************/
  1464. X#ifdef __STDC__
  1465. Xint IsOmitted(int jul, int localomit)
  1466. X#else
  1467. Xint IsOmitted(jul, localomit)
  1468. Xint jul, localomit;
  1469. X#endif
  1470. X{
  1471. X   int d, m, y;
  1472. X
  1473. X   /* Check if we should omit because of local omit */
  1474. X   if (localomit & 1 << (jul % 7)) return 1;
  1475. X
  1476. X   /* Check if we should omit because of fully-specified global omit */
  1477. X   if (NumFullOmit && my_bsearch(jul, FullOmitArray, NumFullOmit)) return 1;
  1478. X
  1479. X   /* Check if we should omit because of partially-specified global omits */
  1480. X   if (NumPartOmit) {
  1481. X      FromJulian(jul, &d, &m, &y);
  1482. X      if (my_bsearch((m << 5)+d, PartOmitArray, NumPartOmit)) return 1;
  1483. X   }
  1484. X
  1485. X   /* Looks cool - don't omit */
  1486. X   return 0;
  1487. X}
  1488. X
  1489. X/***************************************************************/
  1490. X/*                                                             */
  1491. X/*  my_bsearch                                                 */
  1492. X/*                                                             */
  1493. X/*  A simplified version of bsearch() for people whose library */
  1494. X/*  does not have the full version.  This only works when      */
  1495. X/*  searching a sorted array of integers.                      */
  1496. X/*                                                             */
  1497. X/***************************************************************/
  1498. X#ifdef __STDC__
  1499. Xint *my_bsearch(int key, int *array, int number)
  1500. X#else
  1501. Xint *my_bsearch(key, array, number)
  1502. Xint key, *array, number;
  1503. X#endif
  1504. X{
  1505. X   int top = number - 1;
  1506. X   int bot = 0;
  1507. X   int mid;
  1508. X
  1509. X   while (top >= bot) {
  1510. X      mid = (top+bot)/2;
  1511. X      if (*(array+mid) == key) return array+mid;
  1512. X      else if (*(array+mid) > key) top = mid-1;
  1513. X      else bot = mid+1;
  1514. X   }
  1515. X
  1516. X   /* Oh, well - unsuccessful search.  Return NULL */
  1517. X   return NULL;
  1518. X}
  1519. END_OF_FILE
  1520. if test 10630 -ne `wc -c <'omits.c'`; then
  1521.     echo shar: \"'omits.c'\" unpacked with wrong size!
  1522. fi
  1523. # end of 'omits.c'
  1524. fi
  1525. if test -f 'test.rem' -a "${1}" != "-c" ; then 
  1526.   echo shar: Will not clobber existing file \"'test.rem'\"
  1527. else
  1528. echo shar: Extracting \"'test.rem'\" \(2975 characters\)
  1529. sed "s/^X//" >'test.rem' <<'END_OF_FILE'
  1530. X# Test file for REMIND
  1531. X#
  1532. X# Use this file to test the date calculation routines
  1533. X# of the REMIND program by typing:
  1534. X#
  1535. X# (UNIX version):
  1536. X#     remind -dv test.rem 16 FEB 1991 >& temp 
  1537. X#
  1538. X# (DOS version):
  1539. X#    e2o remind -dv test.rem 16 FEB 1991 > temp
  1540. X#
  1541. X# and comparing the results with test.out.
  1542. X#
  1543. X# The only differences should be the date of last
  1544. X# modification/access
  1545. X
  1546. X# Test each possible case of the basic reminders.
  1547. X
  1548. XREM MSG Every Day
  1549. X
  1550. XREM 18 MSG Every 18th 
  1551. XREM 15 MSG Every 15th
  1552. X
  1553. XREM Feb MSG February
  1554. XREM Jan MSG January
  1555. XREM March MSG March
  1556. X
  1557. XREM 13 Jan MSG 13 Jan
  1558. XREM 15 Feb MSG 15 Feb
  1559. XREM 28 Feb MSG 28 Feb
  1560. XREM 29 Feb MSG 29 Feb
  1561. XREM 5 Mar MSG 5 Mar
  1562. X
  1563. XREM 1990 MSG 1990
  1564. XREM 1991 MSG 1991
  1565. XREM 1992 MSG 1991
  1566. X
  1567. XREM 1 1990 MSG 1 1990
  1568. XREM 29 1991 MSG 29 1991
  1569. XREM 29 1992 MSG 29 1992
  1570. XREM 16 1991 MSG 16 1991
  1571. X
  1572. XREM Jan 1990 MSG Jan 1990
  1573. XREM Feb 1991 MSG Feb 1991
  1574. XREM Dec 1991 MSG Dec 1991
  1575. XREM May 1992 MSG May 1992
  1576. X
  1577. XREM 1 Jan 1991 MSG 1 Jan 1991
  1578. XREM 16 Feb 1991 MSG 16 Feb 1991
  1579. XREM 29 Dec 1992 MSG 29 Dec 1992
  1580. X
  1581. XREM Sun MSG Sun
  1582. XREM Fri Sat Tue MSG Fri Sat Tue
  1583. X
  1584. XREM Sun 16 MSG Sun 16
  1585. XREM Mon Tue Wed Thu Fri 1 MSG Mon Tue Wed Thu Fri 1
  1586. X
  1587. XREM Sun Feb MSG Sun Feb
  1588. XREM Mon Tue March MSG Mon Tue March
  1589. X
  1590. XREM Sun 16 Feb MSG Sun 16 Feb
  1591. XREM Mon Tue 10 March MSG Mon Tue 10 March
  1592. X
  1593. XREM Sat Sun 1991 MSG Sat Sun 1991
  1594. XREM Mon Tue 1992 MSG Mon Tue 1992
  1595. X
  1596. XREM Sun 16 1991 MSG Sun 16 1991
  1597. XREM Mon Tue Wed Thu Fri 1 1992 MSG Mon Tue Wed Thu Fri 1 1992
  1598. X
  1599. XREM Mon Feb 1991 MSG Mon Feb 1991
  1600. XREM Tue Jan 1992 MSG Tue Jan 1992
  1601. X
  1602. XREM Sun Mon 16 Feb 1991 MSG Sun Mon 16 Feb 1991
  1603. XREM Tue 28 Jan 1992 MSG Tue 28 Jan 1992
  1604. X
  1605. X# Try some Backs
  1606. XCLEAR-OMIT-CONTEXT
  1607. XREM 1 -1 OMIT sat sun MSG 1 -1 OMIT Sat Sun
  1608. XREM 1 --1 OMIT sat sun MSG 1 --1 OMIT Sat Sun
  1609. X
  1610. XOMIT 28 Feb
  1611. XREM 1 -1 OMIT sat sun MSG 1 -1 OMIT Sat Sun (28 Feb omitted)
  1612. XREM 1 --1 OMIT sat sun MSG 1 --1 OMIT Sat Sun (28 Feb omitted)
  1613. X
  1614. XCLEAR-OMIT-CONTEXT
  1615. X
  1616. X# Try out UNTIL
  1617. XREM Wed UNTIL 21 Feb 1991 MSG Wed UNTIL 21 Feb 1991
  1618. X
  1619. X# Try playing with the OMIT context
  1620. X
  1621. XOMIT 28 Feb 1991
  1622. XREM 1 Mar -1 MSG 1 mar -1 (28feb91 omitted)
  1623. XREM 1 Mar --1 MSG 1 mar --1 (28Feb91 omitted)
  1624. XREM 28 Feb BEFORE MSG 28 Feb BEFORE (28Feb91 omitted)
  1625. XREM 28 Feb SKIP MSG 28 Feb SKIP (28Feb91 omitted)
  1626. XREM 28 Feb AFTER MSG 28 Feb AFTER (28Feb91 omitted)
  1627. X
  1628. XPUSH-OMIT-CONTEXT
  1629. XCLEAR-OMIT-CONTEXT
  1630. XREM 1 Mar -1 MSG 1 mar -1
  1631. XREM 1 Mar --1 MSG 1 mar --1
  1632. XREM 28 Feb BEFORE MSG 28 Feb BEFORE
  1633. XREM 28 Feb SKIP MSG 28 Feb SKIP 
  1634. XREM 28 Feb AFTER MSG 28 Feb AFTER
  1635. X
  1636. XPOP-OMIT-CONTEXT
  1637. XREM 1 Mar -1 MSG 1 mar -1 (28feb91 omitted)
  1638. XREM 1 Mar --1 MSG 1 mar --1 (28Feb91 omitted)
  1639. XREM 28 Feb BEFORE MSG 28 Feb BEFORE (28Feb91 omitted)
  1640. XREM 28 Feb SKIP MSG 28 Feb SKIP (28Feb91 omitted)
  1641. XREM 28 Feb AFTER MSG 28 Feb AFTER (28Feb91 omitted)
  1642. X
  1643. X
  1644. XREM 13 March 1991 *1 UNTIL 19 March 1991 MSG 13-19 Mar 91
  1645. X# Test BACK
  1646. XCLEAR-OMIT-CONTEXT
  1647. XREM 18 Feb 1991 +1 MSG 18 Feb 1991 +1
  1648. X
  1649. XOMIT 17 Feb 1991
  1650. XREM 18 Feb 1991 +1 MSG 18 Feb 1991 +1 (17Feb91 omitted)
  1651. XREM 18 Feb 1991 ++1 MSG 18 Feb 1991 ++1 (17Feb91 omitted)
  1652. X
  1653. XCLEAR-OMIT-CONTEXT
  1654. X
  1655. END_OF_FILE
  1656. if test 2975 -ne `wc -c <'test.rem'`; then
  1657.     echo shar: \"'test.rem'\" unpacked with wrong size!
  1658. fi
  1659. # end of 'test.rem'
  1660. fi
  1661. if test -f 'timed.c' -a "${1}" != "-c" ; then 
  1662.   echo shar: Will not clobber existing file \"'timed.c'\"
  1663. else
  1664. echo shar: Extracting \"'timed.c'\" \(7484 characters\)
  1665. sed "s/^X//" >'timed.c' <<'END_OF_FILE'
  1666. X#include <stdio.h>
  1667. X#include <signal.h>
  1668. X#include <string.h>
  1669. X#ifndef NO_MALLOC_H
  1670. X#include <malloc.h>
  1671. X#endif
  1672. X#include "defines.h"
  1673. X#include "globals.h"
  1674. X#include "protos.h"
  1675. X
  1676. X/***************************************************************/
  1677. X/*                                                             */
  1678. X/*  TIMED.C                                                    */
  1679. X/*                                                             */
  1680. X/*  Contains routines for triggering timed reminders           */
  1681. X/*                                                             */
  1682. X/*  By David Skoll - 12 Nov 1990                               */
  1683. X/*                                                             */
  1684. X/*  (C) 1990 by David Skoll                                    */
  1685. X/*                                                             */
  1686. X/***************************************************************/
  1687. X
  1688. X/* Global pointer to AT reminders */
  1689. Xstatic AtEntry AtQueue =
  1690. X{
  1691. X   0, 0, 0, 0, Unknown_t, NULL, NULL
  1692. X};
  1693. X
  1694. X/***************************************************************/
  1695. X/*                                                             */
  1696. X/*  AddEntry                                                   */
  1697. X/*                                                             */
  1698. X/*  Add an entry to the AT queue, keeping things sorted by     */
  1699. X/*  trigger time.                                              */
  1700. X/*                                                             */
  1701. X/*  Returns 0 for success, nonzero for failure                 */
  1702. X/*                                                             */
  1703. X/***************************************************************/
  1704. X#ifdef __STDC__
  1705. Xint AddEntry(AtEntry *e)
  1706. X#else
  1707. Xint AddEntry(e)
  1708. XAtEntry *e;
  1709. X#endif
  1710. X{
  1711. X   AtEntry *current, *prev;
  1712. X   prev = &AtQueue;
  1713. X   current = prev->next;
  1714. X   while (current) {
  1715. X      if (e->firsttime < current->firsttime) {
  1716. X         prev->next = e;
  1717. X         e->next = current;
  1718. X         break;
  1719. X      } else {
  1720. X         prev = current;
  1721. X         current = prev->next;
  1722. X      }
  1723. X   }
  1724. X   if (!current) {
  1725. X      prev->next = e;
  1726. X      e->next = NULL;
  1727. X   }
  1728. X}
  1729. X
  1730. X/***************************************************************/
  1731. X/*                                                             */
  1732. X/* DoAt                                                        */
  1733. X/* Creates an entry for an At reminder, puts it on the queue   */
  1734. X/* Updates the global variable NumAtsQueued                    */
  1735. X/*                                                             */
  1736. X/***************************************************************/
  1737. X#ifdef __STDC__
  1738. Xint DoAt(int tim, int tdelta, int trep, char *body, enum Token_t type)
  1739. X#else
  1740. Xint DoAt(tim, tdelta, trep, body, type)
  1741. Xint tim, tdelta, trep;
  1742. Xchar *body;
  1743. Xenum Token_t type;
  1744. X#endif
  1745. X{
  1746. X   AtEntry *e;
  1747. X   int curtime;
  1748. X
  1749. X   curtime = (int) (SystemTime() / 60);
  1750. X   
  1751. X   /* If the trigger time is in the past, don't add to the at queue */     
  1752. X   if (tim < curtime) return 0;
  1753. X
  1754. X   /* Allocate space for the entry */
  1755. X   e = (AtEntry *) malloc(sizeof(AtEntry));
  1756. X   if (e == (AtEntry *) NULL) {
  1757. X      Eprint("Can't malloc memory for AT!\n");
  1758. X      return 1;
  1759. X   }
  1760. X   e->text = malloc(strlen(body)+1);
  1761. X   if (e->text == NULL) {
  1762. X      Eprint("Can't malloc memory for body of AT!\n");
  1763. X      return 1;
  1764. X   }
  1765. X   strcpy(e->text, body);
  1766. X
  1767. X   /* Find out the next trigger time */
  1768. X   e->firsttime = FindNextTriggerTime(tim, trep, tdelta, curtime);
  1769. X   e->repeat    = trep;
  1770. X   e->type      = type;
  1771. X   e->time      = tim;
  1772. X   e->delta     = tdelta;
  1773. X   AddEntry(e);
  1774. X   NumAtsQueued++;
  1775. X   return 0;
  1776. X}
  1777. X
  1778. X/***************************************************************/
  1779. X/*                                                             */
  1780. X/* int FindNextTriggerTime                                     */
  1781. X/*                                                             */
  1782. X/* Returns the next time a queued AT should be triggered.      */
  1783. X/* Returns -1 if the AT has expired.                           */
  1784. X/*                                                             */
  1785. X/***************************************************************/
  1786. X#ifdef __STDC__
  1787. Xint FindNextTriggerTime(int tim, int rep, int delta, int curtime)
  1788. X#else
  1789. Xint FindNextTriggerTime(tim, rep, delta, curtime)
  1790. Xint tim, rep, delta, curtime;
  1791. X#endif
  1792. X{
  1793. X   int trigger = tim;
  1794. X   
  1795. X   if (delta <= 0)
  1796. X      if (trigger < curtime) return -1; else return trigger;
  1797. X   
  1798. X   trigger -= delta;
  1799. X   if (rep == -1) rep = delta;
  1800. X
  1801. X   if (trigger < curtime) trigger += ((curtime - trigger) / rep) * rep;
  1802. X   if (trigger < curtime) trigger += rep;
  1803. X   if (trigger > tim) trigger = tim;
  1804. X   if (trigger < curtime) return -1; else return trigger;
  1805. X}
  1806. X
  1807. X/***************************************************************/
  1808. X/*                                                             */
  1809. X/* HandleQueuedAts                                             */
  1810. X/*                                                             */
  1811. X/* Handles all the queued AT reminders.  Sits in a sleep loop  */
  1812. X/* to trigger reminders.                                       */
  1813. X/*                                                             */
  1814. X/***************************************************************/
  1815. X#ifdef __STDC__
  1816. Xvoid HandleQueuedAts(void)
  1817. X#else
  1818. Xvoid HandleQueuedAts()
  1819. X#endif
  1820. X{
  1821. X   AtEntry *e;
  1822. X   long TimeToSleep;
  1823. X   unsigned SleepTime;
  1824. X   long now;
  1825. X
  1826. X   signal(SIGINT, SigIntHandler);
  1827. X
  1828. X   while (e = AtQueue.next) {
  1829. X      now = SystemTime();
  1830. X      TimeToSleep = (long) e->firsttime * 60L - now;
  1831. X      if (TimeToSleep < 0L) TimeToSleep = 0L;
  1832. X
  1833. X      /* Be paranoid and assume that unsigneds are only two bytes long -
  1834. X         therefore, do the sleeping in 30000-second chunks. */
  1835. X
  1836. X      while (TimeToSleep) {
  1837. X         if (TimeToSleep > 30000L) SleepTime = 30000;
  1838. X                              else SleepTime = (unsigned) TimeToSleep;
  1839. X         sleep(SleepTime);
  1840. X         TimeToSleep -= (long) SleepTime;
  1841. X      }
  1842. X
  1843. X      /* Over here, we trigger the reminder */
  1844. X      DoSubst(e->text, WorkBuf, CurDay, CurMon, CurYear, JulianToday, e->type, e->time, 0);
  1845. X      if (e->type == Run_t) system(WorkBuf);
  1846. X      else printf("%s\n", WorkBuf);
  1847. X
  1848. X      /* Remove the entry from the queue */
  1849. X      AtQueue.next = e->next;
  1850. X
  1851. X      /* Check if this reminder should be re-triggered */
  1852. X      e->firsttime = FindNextTriggerTime(e->time, e->repeat, 
  1853. X                                        e->delta, e->firsttime + 1);
  1854. X
  1855. X      if (e->firsttime != -1) AddEntry(e);
  1856. X      else {
  1857. X      /* Not to be added - free the memory */
  1858. X         free(e->text);
  1859. X         free(e);
  1860. X      }
  1861. X   }
  1862. X}
  1863. X
  1864. X/***************************************************************/
  1865. X/*                                                             */
  1866. X/* SigIntHandler                                               */
  1867. X/*                                                             */
  1868. X/* For debugging purposes, when sent a SIGINT, we print the    */
  1869. X/* contents of the queue.                                      */
  1870. X/*                                                             */
  1871. X/***************************************************************/
  1872. Xvoid SigIntHandler()
  1873. X{
  1874. X   AtEntry *e;
  1875. X
  1876. X   printf("Contents of AT queue:\n");
  1877. X
  1878. X   e = AtQueue.next;
  1879. X   while (e) {
  1880. X      printf("Trigger: %02d:%02d  Activate: %02d:%02d  Rep: %d  Delta: %d\n",
  1881. X              e->time / 60, e->time % 60, e->firsttime / 60, e->firsttime % 60,
  1882. X              e->repeat, e->delta);
  1883. X      printf("Text: %s %s\n\n", ((e->type == Msg_t) ? "MSG" : "RUN"), e->text);
  1884. X      e = e->next;
  1885. X   }
  1886. X   printf("\n");
  1887. X}
  1888. END_OF_FILE
  1889. if test 7484 -ne `wc -c <'timed.c'`; then
  1890.     echo shar: \"'timed.c'\" unpacked with wrong size!
  1891. fi
  1892. # end of 'timed.c'
  1893. fi
  1894. echo shar: End of archive 3 \(of 4\).
  1895. cp /dev/null ark3isdone
  1896. MISSING=""
  1897. for I in 1 2 3 4 ; do
  1898.     if test ! -f ark${I}isdone ; then
  1899.     MISSING="${MISSING} ${I}"
  1900.     fi
  1901. done
  1902. if test "${MISSING}" = "" ; then
  1903.     echo You have unpacked all 4 archives.
  1904.     rm -f ark[1-9]isdone
  1905. else
  1906.     echo You still need to unpack the following archives:
  1907.     echo "        " ${MISSING}
  1908. fi
  1909. ##  End of shell archive.
  1910. exit 0
  1911. exit 0 # Just in case...
  1912. -- 
  1913. Kent Landfield                   INTERNET: kent@sparky.IMD.Sterling.COM
  1914. Sterling Software, IMD           UUCP:     uunet!sparky!kent
  1915. Phone:    (402) 291-8300         FAX:      (402) 291-4362
  1916. Please send comp.sources.misc-related mail to kent@uunet.uu.net.
  1917.